/*****************************************************************************
* File Name: main.c
*
* Version 1.0
*
* Description:
*  This file handles the init sequence and port configurations for USB Bridge.
*
* Owner:
* SIRK
*
* Related Document:
*
* Hardware Dependency:
* CY5670 CySmart USB Dongle
*
* Code Tested With:
* 1. PSoC Creator 3.1 
* 2. ARM GCC 4.8.4
*
******************************************************************************
* Copyright (2015), Cypress Semiconductor Corporation.
******************************************************************************
* This software is owned by Cypress Semiconductor Corporation (Cypress) and is
* protected by and subject to worldwide patent protection (United States and
* foreign), United States copyright laws and international treaty provisions.
* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable
* license to copy, use, modify, create derivative works of, and compile the
* Cypress Source Code and derivative works for the sole purpose of creating
* custom software in support of licensee product to be used only in conjunction
* with a Cypress integrated circuit as specified in the applicable agreement.
* Any reproduction, modification, translation, compilation, or representation of
* this software except as specified above is prohibited without the express
* written permission of Cypress.
*
* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH
* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* Cypress reserves the right to make changes without further notice to the
* materials described herein. Cypress does not assume any liability arising out
* of the application or use of any product or circuit described herein. Cypress
* does not authorize its products for use as critical components in life-support
* systems where a malfunction or failure may reasonably be expected to result in
* significant injury to the user. The inclusion of Cypress' product in a life-
* support systems application implies that the manufacturer assumes all risk of
* such use and in doing so indemnifies Cypress against all charges. Use may be
* limited by and subject to the applicable Cypress software license agreement.
*****************************************************************************/

#include "main.h"
#include "Interrupts.h"

/*This flag indicates, USB reset signal, will be set in ISR*/
volatile bool USBResetDetected  = false;

/* USB Serial Number String Max. Length = LEN + DTYPE + 2*PREFIX + 2*NUMBER = 40 Bytes */
#define SN_LENGTH_MAX           (0x28u)

/*XRES PRoC BLE*/
#define XRES_PROC_BLE    XRES_SetDriveMode(XRES_DM_STRONG);        \
                         XRES_Write(0);                            \
                         CyDelayUs(100);                           \
                         XRES_SetDriveMode(XRES_DM_DIG_HIZ);

/* USB Serial Number String */
uint8 snString[SN_LENGTH_MAX];

/*This flag indicates, USB suspend signal, will be set in ISR*/
volatile static bool checkSuspendState = false;

/*******************************************************************************
* Function Name: isr_UsbSuspend
********************************************************************************
*
* Summary:
*  ISR Callback function for USB suspend timer
*
* Parameters:
*  NONE
*
* Return:
*  NONE
*
* Theory:
*  NONE
*
* Side Effects:
*  Modifies checkSuspendState flag
*
* Note:
*
*******************************************************************************/
CY_ISR(isr_UsbSuspend)
{
    checkSuspendState = true;
    Timer_UsbSuspend_ReadStatusRegister();
}

/*******************************************************************************
* Function Name: Device_Init
********************************************************************************
*
* Summary:
*  Initialize all the system modules
*
* Parameters:
*  NONE
*
* Return:
*  NONE
*
* Theory:
*  NONE
*
* Side Effects:
*  NONE
*
* Note:
*  This API need to be called on each USB reset
*
*******************************************************************************/
static void Device_Init(void)
{   
    Pin_StatusLED_SetDriveMode(Pin_StatusLED_DM_OD_LO);
    Pin_PowerLED_SetDriveMode(Pin_PowerLED_DM_STRONG);

    /* Start USB operation at VDDD specified in the DWR */
    USBFS_Start(0, USBFS_DWR_VDDD_OPERATION);

    ConfigureAudioPath();

    /* Initializes the timer for handling the USBFS Suspend Activity */
    Timer_UsbSuspend_Start();

    isr_UsbSuspend_StartEx(isr_UsbSuspend);
    
    isr_RemoteWakeup_StartEx(isr_RemoteWakeup);

    /* Initialize variables*/
    USBCommInit();
    CYGlobalIntEnable;

    while(!USBFS_GetConfiguration())
    {
    }

    /* Indicate successful enumeration. */
    Pin_StatusLED_SetDriveMode(Pin_StatusLED_DM_STRONG);    

    /* USBResetDetected ' flag is set to false to indicate that the USB is 
        * connected and configured. This flag will be set inside USBFS_episr.c if 
        * the USB Bus Reset is detected */
    USBResetDetected  = false;

    UART_Start();

    /*Start isr for packet decoder time-out */
    isr_PktDecoder_StartEx(isr_PktDecoder);

#if defined(USBFS_ENABLE_CDC_CLASS)
    /* Starts the UART, sets Tx Pin to HiZ to save power during enumeration.*/
    USBUARTInit();

    /* Initializes CDC Interface, Sets Tx Pin to Strong Drive. */
    USBUARTStart();
#endif

    /*Reset PRoC BLE*/
    XRES_PROC_BLE;
}

/*******************************************************************************
* Function Name: GenerateUSBSerialNumberString
********************************************************************************
*
* Summary:
*  This function fills parameter string descr with unique values regarding to die ID. 
* This number is 16-digit number in hexadecimal string format e.g. 1F19172E03242400. 
* Also it allocates the "BLE" prefix before the number.
*
* Parameters:
*  uint8 * descr
*
* Return:
*  NONE
*
* Theory:
*  NONE
*
* Side Effects:
*  NONE
*
* Note:
*  The string "BLE" is required as part of SN, for CySmart tool detection
*******************************************************************************/
static void GenerateUSBSerialNumberString(uint8 * descr)
{
    uint8 i = 0u;
    uint8 j = 0u;
    uint8 k = 0u;
    uint8 id_byte;
    const char8 CYCODE hex[16u] = "0123456789ABCDEF";

    /* Check descriptor validation */
    if (descr != NULL)
    {
        descr[k++] = 0x28u;
        descr[k++] = USBFS_DESCR_STRING;
        descr[k++] = 'B';
        descr[k++] = 0;
        descr[k++] = 'L';
        descr[k++] = 0;
        descr[k++] = 'E';
        descr[k++] = 0;

        /* put unique die ID into descriptor following string descriptor type format */
        for (i = 0u; i < 16u; i++)
        {
            id_byte = CY_GET_XTND_REG8((void CYFAR *)(USBFS_DIE_ID + i));
            descr[k + j++] = (uint8)hex[id_byte >> 4u];
            descr[k + j++] = 0u;
            descr[k + j++] = (uint8)hex[id_byte & 0x0Fu];
            descr[k + j++] = 0u;
        }
    }
}

/*******************************************************************************
* Function Name: SendPSoCToSleep()
********************************************************************************
* Summary:
*  Prepares the PSoC 5LP device for sleep and puts the device in low power mode.
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
static void SendPSoCToSleep(void)
{
    /*Indicate suspend to PRoC BLE*/
    CyPins_ClearPin(Pin_BLE_Extra_0);

    /*Switch OFF the power LED*/
    Pin_PowerLED_SetDriveMode(Pin_PowerLED_DM_OD_LO);

    /*Switch OFF the status LED*/
    Pin_StatusLED_SetDriveMode(Pin_StatusLED_DM_OD_LO);

    /*Stop timer and isr*/
    Timer_UsbSuspend_Stop();
    isr_PktDecoder_Stop();

    CyPmSaveClocks();
    CyPmSleep(PM_SLEEP_TIME_NONE, PM_SLEEP_SRC_PICU);
}

/*******************************************************************************
* Function Name: ResumeFromSleep()
********************************************************************************
* Summary:
*  Restores the configurations of PSoC 5LP after resuming from sleep.
*
* Parameters:
*  None
*
* Return:
*  None
*
* Side affects: 
*
*******************************************************************************/
static void ResumeFromSleep(void)
{
    CyPmRestoreClocks();
    isr_PktDecoder_StartEx(isr_PktDecoder);
    /* Initializes the timer for handling the USBFS Suspend Activity */
    Timer_UsbSuspend_Start();
    CyPins_SetPin(Pin_BLE_Extra_0);
    Pin_PowerLED_SetDriveMode(Pin_PowerLED_DM_STRONG);
    Pin_StatusLED_SetDriveMode(Pin_StatusLED_DM_STRONG);
}

/*******************************************************************************
* Function Name: HandleUSBSuspend()
********************************************************************************
* Summary:
*     Handles the USB suspend Event
*
* Parameters:
*  None
*
* Return:
*  None
*
* Theory:
*  USB Bus activity need to be checked periodically to detect if the device has been suspended by Host
*
* Side affects: Clears the global variable "checkSuspendState". This variable is 
* used to check if 10ms has passed since the last time USB activity was checked.
*
*******************************************************************************/
static void HandleUSBSuspend(void)
{
    if(checkSuspendState)
    {
        if(USBFS_GetConfiguration() && (USBFS_CheckActivity() == 0))
        {
            USBFS_Suspend();
            SendPSoCToSleep();
            ResumeFromSleep();
            USBFS_Resume();

            /*Check and handle remote wakeup, if supported by the HID device*/
            if(true == USBFS_RWUEnabled())
            {
                uint8 enableInterrupts, i;
                enableInterrupts = CyEnterCriticalSection();

                /* Issue a K State */
                USBFS_Force(USBFS_FORCE_K);

                /*Hold the resume signal for at least 1ms, but no longer than 15ms.  
                             We will wait for ~10ms, or until we see USB activity*/
                for(i=0; i<100; i++)
                {
                    CyDelayUs(100);

                    /*Hubs require a minimum of 1ms before disabling K state, 
                                   even if USB activity is detected within 1ms */
                    if ((i > 10) && USBFS_bCheckActivity())
                    {
                        /*USB activity detected, break out of loop*/
                        break;
                    }
                }

                /* Remove K State from Bus */ 
                USBFS_Force(USBFS_FORCE_NONE);

                CyExitCriticalSection(enableInterrupts);
            }

#ifdef WINDOWS7_WORKAROUND
            if(USBFS_GetConfiguration() != 0)
            {
                USBFS_configurationChanged = USBFS_TRUE;
                USBFS_Config(USBFS_FALSE);
            }
#endif
        }
        else
        {
            /* Do nothing */
        }
        checkSuspendState = false;
        /* CDT#188737 - Reset the timer flag on each USB activity check to avoid multiple reads in single slot*/
        Timer_UsbSuspend_Reset_Write(1u);
    }
}

/*******************************************************************************
* Function Name: main
********************************************************************************
*
* Summary:
*  Handles monitoring of suspend, UART commands and BLE state machine
*
* Parameters:
*  None
*
* Return:
*  int
*
* Theory:
*  None
*
* Side Effects:
*  None
*
* Note:
*
*******************************************************************************/
int main()
{     
    CyGlobalIntEnable;

    /* Generate unique serial number string and allocate specific prefix, if needed. */
    GenerateUSBSerialNumberString((uint8*)snString);

    /* Pass serial number string to the call-back function. */
    USBFS_SerialNumString((uint8*)snString);

    while(1)
    {
        /*Initialize the clock for UART baud rate*/
        Clock_1_SetDivider(DIVIDER500000 - 1);

        /*Initialize resources on every USB reset*/
        Device_Init();

        /*Check for USB RESET - useful for self power devices*/
        while(!USBResetDetected)
        {
            ServiceUSB();

            /* Clear audio IN buffer when IN stream is stopped by Host*/
            HandleAudioInDisable();

#if defined(USBFS_ENABLE_CDC_CLASS)
            /* Handles USB to UART comm */
            UARTTransmit();
#endif

            /*Handle UART to USB communication*/
            HandleUSBComm();

            /* Handles the USB Suspend Event */
            HandleUSBSuspend();
        }

        if(USBResetDetected)
        {
            /* If reset, stop the component and try to re-enumerate. 
                     * Set to true inside USBFS_episr.c [CY_ISR(USBFS_BUS_RESET_ISR)] */
            USBFS_Stop();

            /* Disables the timer for handling the USBFS Suspend Activity */
            Timer_UsbSuspend_Stop();

            isr_UsbSuspend_Stop();
        }
    }
}

/* [] END OF FILE */

